require 'xapian'

module Mil
  module Index
    # This class provides an indexer that can be used to
    # add an entry in the index database. The index 
    # database provides a linking between file name and tags
    # to addresses of matching files.
    class Index
      OBJECT_NAME = 0
      CREATED = 1
      LAST_UPDATED = 2

      def initialize(opts={})
	if opts[:database]
	  @path = opts[:database]
	  _check_path
	end
      end

      # Used to open the connections to the Xapian Database.
      # If already initially open, reopen the database to ensure
      # that fresh representation of the database is used.
      def open(database='')
	@path ||= database
	_check_path

	if database.empty? and @db
	  @db.reopen
	else
	  _open(@path)
	end

	@open = true
      end

      # Will generate a Xapian Document from the passed fields
      def generate_doc(address, name, namespace = [], tags = [])
	unless namespace.is_a? Array
	  namespace = [ namespace.to_s ]
	end

	unless tags.is_a? Array
	  if tags.nil?
	    tags = []
	  else
	    tags = [ tags.to_s ]
	  end
	end

	doc = Xapian::Document.new
	@term_generator.document = doc

	doc.data = address

	# --
	# always include the global namespace
	# ++
	unless namespace.include? 'global'
	  namespace << ['global']
	end

	# --
	# index by type
	# ++
	doc.add_term("I" + address)
	doc.add_term("N" + name)
	namespace.each { |n|
	  doc.add_term("S" + n)
	}

	unless tags.empty?
	  tags.each { |t|
	    @term_generator.increase_termpos
	    @term_generator.index_text_without_positions("#{t}", 1, "T" )
	  }
	end

	# --
	# index without type prefix for generic search
	# ++
	tag_str = "#{name} #{namespace.join(' ')} #{tags.join(' ')}"
	@term_generator.index_text_without_positions(tag_str, 1)

	doc.add_value(OBJECT_NAME, name)

	doc
      end

      # Write all Xapian Documents passed to the database.
      def save(docs)
	begin
	  if docs.is_a? Array
	    docs.each { |d|
	      @db.replace_document("I" + d.data, d)
	    }
	  elsif docs.is_a? Xapian::Document
	    @db.replace_document("I" + docs.data, docs)
	  else
	    raise ArgumentError
	  end
	rescue Exception => e
	  p "Had an error:#{e} \n #{e.backtrace}"
	else
	  @db.flush
	end
      end

      private
      def _check_path
	if @path
	  FileUtils.mkpath @path unless File.exist? @path
	end
      end

      def _open(database = '')
	@path = database unless database.empty?

	@db = Xapian::WritableDatabase.new(database, Xapian::DB_CREATE_OR_OPEN)

	@stemmer = Xapian::Stem.new('english')

	@term_generator = Xapian::TermGenerator.new()
	@term_generator.set_flags(Xapian::TermGenerator::FLAG_SPELLING, 0)
	@term_generator.database = @db
	@term_generator.stemmer = @stemmer

      end

    end
  end
end
